package tbs.framework.boardcast.obsever.impl;

import cn.hutool.extra.spring.SpringUtil;
import lombok.Getter;
import tbs.framework.boardcast.notify.INotifying;
import tbs.framework.boardcast.notify.impl.SimpleNotifying;
import tbs.framework.boardcast.obsever.IObserver;
import tbs.framework.utils.CollUtils;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 抽象类型观察者 提供基于类型处理的通知响应基础实现，用于构建特定类型的观察者模式组件
 *
 * @author tongj
 */
public class TypeableObserver implements IObserver {
    /**
     * 自定义类型观察者接口 定义处理特定类型通知的基本操作，作为构建具体观察者的通用契约
     *
     * @param <T> 通知数据的类型
     */
    public static interface ICustomTypeObserver<T> {

        /**
         * 获取当前观察者支持的通知类型标识
         *
         * @return 返回整数类型的事件类型标识
         */
        Integer getSupportedType();

        /**
         * 获取当前观察者支持的主体ID集合
         *
         * @return 返回支持的主体ID集合
         */
        Set<Long> getSupportedSubjectIdSet();

        /**
         * 处理指定类型的通知数据
         *
         * @param t 需要处理的通知数据对象
         */
        void handle(T t);
    }

    /**
     * 创建型观察者接口 专门处理创建类事件的观察者契约定义
     *
     * @param <T> 创建事件相关的数据类型
     */
    public static interface IOnCreateTypeObserver<T> extends ICustomTypeObserver<T> {
        @Override
        default Integer getSupportedType() {
            return SimpleNotifying.CREATE;
        }
    }

    /**
     * 更新型观察者接口 专门处理更新类事件的观察者契约定义
     *
     * @param <T> 更新事件相关的数据类型
     */
    public static interface IOnUpdateTypeObserver<T> extends ICustomTypeObserver<T> {
        @Override
        default Integer getSupportedType() {
            return SimpleNotifying.UPDATE;
        }
    }

    /**
     * 删除型观察者接口 专门处理删除类事件的观察者契约定义
     *
     * @param <T> 删除事件相关的数据类型
     */
    public static interface IOnDeleteTypeObserver<T> extends ICustomTypeObserver<T> {
        @Override
        default Integer getSupportedType() {
            return SimpleNotifying.DELETE;
        }
    }

    /**
     * 存储已注册观察者的映射表，按类型进行索引 使用@Getter注解自动生成getter方法
     */
    @Getter
    private Map<Integer, List<ICustomTypeObserver>> customTypeObserverMap;

    /**
     * 构造函数，初始化观察者映射关系
     *
     * @param customTypeObserverList 需要注册的观察者列表
     */
    public TypeableObserver(List<ICustomTypeObserver> customTypeObserverList, Set<Long> supportedSubjectId) {
        this.supportedSubjectId = supportedSubjectId;
        this.customTypeObserverMap = customTypeObserverList.stream().filter(iCustomTypeObserver -> {
            return iCustomTypeObserver.getSupportedSubjectIdSet().containsAll(supportedSubjectId);
        }).collect(Collectors.groupingBy(ICustomTypeObserver::getSupportedType));
    }

    public TypeableObserver(Set<Long> supportedSubjectId) {
        this(SpringUtil.getBeansOfType(ICustomTypeObserver.class).values().stream().collect(Collectors.toList()),
            supportedSubjectId);
    }

    public TypeableObserver(Long... supportedSubjectIds) {
        this(Arrays.stream(supportedSubjectIds).map(Long::valueOf).distinct().collect(Collectors.toSet()));
    }

    private Set<Long> supportedSubjectId;

    /**
     * 获取当前观察者支持的主体ID集合 用于判断是否需要处理特定主体的通知事件
     *
     * @return 返回支持的主体ID集合
     */
    @Override
    public Set<Long> getSupportedSubjectIdSet() {
        return supportedSubjectId;
    }

    /**
     * 处理通知事件的核心方法 根据通知类型和主体ID选择合适的观察者进行处理
     *
     * @param notifying 包含通知信息的对象
     */
    @Override
    public void handle(INotifying notifying) {
        if (!getSupportedSubjectIdSet().contains(notifying.getSubject().getSubjectId())) {
            return;
        }
        List<ICustomTypeObserver> customTypeObservers = getCustomTypeObserverMap().get(notifying.getType());
        if (CollUtils.isEmpty(customTypeObservers)) {
            return;
        }
        for (ICustomTypeObserver customTypeObserver : customTypeObservers) {
            if (!customTypeObserver.getSupportedSubjectIdSet().contains(notifying.getSubject().getSubjectId())) {
                continue;
            }
            customTypeObserver.handle(notifying.getData());
        }
    }
}
